package de.koller.worlders.engine.entity.enc.pathfinding;

import de.koller.worlders.WorldersRoot;
import java.util.*;

public class GUPathFinder {

	public static final int MAX_DISTANCE = 30;

	private final Map<String, TileNode> buffer = new HashMap<>();
	private final PriorityQueue<TileNode> openlist = new PriorityQueue<>();
	private final ArrayList<TileNode> closedlist = new ArrayList<>();
	private TileNode fromNode;
	
	private final WorldersRoot wroot;
	
	public GUPathFinder( WorldersRoot wroot ) {
		this.wroot = wroot;
	}
	
	public List<WayEntity> way( WayEntity from, WayEntity to, IGUMover m ) {
		//@todo sync when ready
		if( wroot.getTileMap().tileHolder.isLocked() )
			return null;
		
		TileNode t = findWay( from, to, m );
		if( t == null )
			return null;
		
		LinkedList<WayEntity> way = new LinkedList<>();
		way.addFirst( new WayEntity( t.tx, t.ty ) );
		
		do {
			t = t.predecessor;
			way.addFirst( new WayEntity( t.tx, t.ty ) );
		} while( t.predecessor != null );
		return way;
	}
	
	private TileNode findWay( WayEntity from, WayEntity to, IGUMover m ) {
		buffer.clear();
		
		if( from.equals( to ) )
			return null;
		
		TileNode dest = TileNode.create( wroot, to.tx, to.ty, buffer );
		if( dest.isBlocked( dest, buffer ) )
			return null;
		
		openlist.clear();
		closedlist.clear();
		
		fromNode = TileNode.create( wroot, from.tx, from.ty, buffer );
		openlist.add( fromNode );
		
		do {
			TileNode currentNode = openlist.poll();
			
			if( currentNode.equals( dest ) )
				return dest;
			
			this.expandNode( currentNode, dest );
			closedlist.add( currentNode );
		} while( !openlist.isEmpty() );
		
		return null;
	}

	private void expandNode( TileNode current, TileNode dest ) {
		TileNode[] suc = new TileNode[ 8 ];
		suc[0] = TileNode.create( wroot, current.tx+1, current.ty, buffer );
		suc[1] = TileNode.create( wroot, current.tx-1, current.ty, buffer );
		suc[2] = TileNode.create( wroot, current.tx, current.ty+1, buffer );
		suc[3] = TileNode.create( wroot, current.tx, current.ty-1, buffer );
		//diagonal
		suc[4] = TileNode.create( wroot, current.tx+1, current.ty+1, buffer );
		suc[5] = TileNode.create( wroot, current.tx-1, current.ty-1, buffer );
		suc[6] = TileNode.create( wroot, current.tx+1, current.ty-1, buffer );
		suc[7] = TileNode.create( wroot, current.tx-1, current.ty+1, buffer );
		
		for( int i = 0; i < suc.length; i++ ) {
			if( Math.abs( fromNode.tx - suc[i].tx ) > MAX_DISTANCE )
				continue;
			if( Math.abs( fromNode.ty - suc[i].ty ) > MAX_DISTANCE )
				continue;
			
 			if( suc[i].isBlocked( current, buffer ) )
				continue;
			
			if( closedlist.contains( suc[i] ) )
				continue;
			
			float cost = suc[i].getWeight( current );
			float depth = current.depth + cost;

			if( openlist.contains( suc[i] ) && depth >= suc[i].depth )
				continue;
			
			suc[i].predecessor = current;
			suc[i].depth = depth;
	
			openlist.remove( suc[i] );
			suc[i].f = depth + getHeurist( suc[i], dest );
			openlist.add( suc[i] );
		}
	}

	public float getHeurist( TileNode from, TileNode to ) {		
		float dx = to.tx - from.tx;
		float dy = to.ty - from.ty;
		float result = (float)( Math.sqrt( (dx*dx) + (dy*dy) ) );
		return result;
	}
	
}
